package com.credithc.signature.job.service;

import cfca.seal.bean.SealUserInfo;
import cfca.seal.util.FileUtil;
import cfca.seal.util.StrategyUtil;
import cfca.seal.util.StringUtil;
import cfca.seal.ws.SealClient;
import com.alibaba.fastjson.JSONObject;
import com.credithc.signature.common.constant.SealConstant;
import com.credithc.signature.common.po.SealApplyInfoDTO;
import com.credithc.signature.common.po.SealInfo;
import com.credithc.signature.common.ro.sign.SealCertInfoRo;
import com.credithc.signature.common.service.CertInfoService;
import com.credithc.signature.common.service.SealInfoService;
import com.credithc.signature.common.service.SignatureLogService;
import com.credithc.signature.common.to.CFCASignParam;
import com.credithc.signature.common.to.CFCASignResult;
import com.credithc.signature.common.to.CFCASignTO;
import com.credithc.signature.common.utils.ErrorCodeHelper;
import com.credithc.signature.common.utils.GeneralUtil;
import com.credithc.signature.common.utils.GenerateUtil;
import com.credithc.signature.common.utils.LogUtil;
import com.credithc.signature.job.configure.props.SignatureProps;
import com.credithc.signature.job.constant.CFCAConstant;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.io.File;
import java.nio.charset.StandardCharsets;
import java.util.Objects;

/**
 * 描述：签章厂商服务<br/>
 * 作者：sunyang
 * 创建：2019/6/5 16:34
 * 版本：v1.0.0<br/>
 */
@Slf4j
@Service
public class CFCASignService {

    @Autowired
    SignatureProps props;

    @Autowired
    private CertInfoService certInfoService;

    @Autowired
    private SealInfoService sealInfoService;

    @Autowired
    SignatureLogService signatureLogService;


    /**
     * 获取公章信息
     *
     * @return
     * @throws Exception
     */
    public SealCertInfoRo querySealInfo(String requestSn, String sealCode) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }

        String result = null;
        long start = System.currentTimeMillis();
        try {
            result = new SealClient(props.sealUrl).getSealInfo(sealCode);
        } catch (Exception e) {
            throw new RuntimeException("查询公章信息失败......");
        }
        long end = System.currentTimeMillis();
        log.debug("－消息－: requestSn={},调用接口查询公章信息,耗时:{}毫秒", requestSn, (end - start));

        // 处理结果
        if (CFCAConstant.CFCA_SUCCESS.equals(StringUtil.getNodeText(result, "Code"))) {
            return new SealCertInfoRo(StringUtil.getNodeText(result, CFCAConstant.SEALPERSON), StringUtil.getNodeText(result, CFCAConstant.SEALORG),
                    StringUtil.getNodeText(result, CFCAConstant.SEALNAME), StringUtil.getNodeText(result, CFCAConstant.SEALCODE),
                    StringUtil.getNodeText(result, CFCAConstant.CERTDN), StringUtil.getNodeText(result, CFCAConstant.CERTSN),
                    StringUtil.getNodeText(result, CFCAConstant.CERTNOTBEFORE), StringUtil.getNodeText(result, CFCAConstant.CERTNOTAFTER));
        } else {
            log.error(LogUtil.outPut(requestSn, "查询公章信息失败,sealCode:" + sealCode));
            return null;
        }
    }

    /**
     * 获取私章信息
     *
     * @return
     * @throws Exception
     */
    public SealCertInfoRo querySealApplyInfo(String requestSn, String userName, String identificationType, String identificationNo) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }

        String result = null;
        long start = System.currentTimeMillis();
        try {
            SealUserInfo sealUserInfo = new SealUserInfo(userName, identificationType, identificationNo, "0");
            String requestXML = StrategyUtil.createSealApplyInfoXML(sealUserInfo);
            result = new SealClient(props.sealUrl).getSealApplyInfo("<requestXML>" + requestXML + "</RequestXML>");

        } catch (Exception e) {
            throw new RuntimeException("查询私章信息失败......");
        }
        long end = System.currentTimeMillis();
        log.debug("－消息－: requestSn={},调用接口查询私章信息,耗时:{}毫秒", requestSn, (end - start));

        // 处理结果
        if (CFCAConstant.CFCA_SUCCESS.equals(StringUtil.getNodeText(result, SealConstant.RETURNCODE))) {
            return new SealCertInfoRo(StringUtil.getNodeText(result, CFCAConstant.SEALPERSON), StringUtil.getNodeText(result, CFCAConstant.SEALORG),
                    StringUtil.getNodeText(result, CFCAConstant.SEALNAME), StringUtil.getNodeText(result, CFCAConstant.SEALCODE),
                    StringUtil.getNodeText(result, CFCAConstant.CERTDN), StringUtil.getNodeText(result, CFCAConstant.CERTSN),
                    StringUtil.getNodeText(result, CFCAConstant.CERTNOTBEFORE), StringUtil.getNodeText(result, CFCAConstant.CERTNOTAFTER));
        } else {
            String errorMsg = StringUtil.getNodeText(result, SealConstant.RETURNMSG);
            log.error(LogUtil.outPut(requestSn, "查询私章信息返回:" + errorMsg + ",userName:" + userName + ",identificationType:" + identificationType + ",identificationNo:" + identificationNo));
            return null;
        }
    }

    /**
     * 更新私章信息
     *
     * @param cfcaSignTO
     * @param requestSn
     * @return
     * @throws Exception
     */
    public CFCASignResult updatePersonalSeal(CFCASignTO cfcaSignTO, String requestSn) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }

        // 1、生成【私章信息】请求参数
        String namedSealInfoXML = GenerateUtil.generatePersonalSealStrategyXML(cfcaSignTO);

        // 2、调用接口更新私章
        String result = null;
        try {
            result = new SealClient(props.sealUrl).autoUpdateNamedSeal(namedSealInfoXML);
        } catch (Exception e) {
            log.error("－消息－: requestSn={},调用接口更新私章失败......", requestSn, e);
            return new CFCASignResult(false, e.getMessage());
        }

        // 3、校验接口返回参数
        if (!checkMakeSealResult(result)) {
            String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(result, CFCAConstant.RETURNCODE), StringUtil.getNodeText(result, CFCAConstant.RETURNMSG));
            if ("未知异常".equals(errorMsg)) {
                if (result.contains("error")) {
                    errorMsg = result;
                }
            }
            return new CFCASignResult(false, result);
        } else {
            return new CFCASignResult(true, result);
        }
    }

    /**
     * 更新过期公章信息
     *
     * @param cfcaSignTO
     * @param requestSn
     * @return
     */
    public CFCASignResult updateOfficeSeal(CFCASignTO cfcaSignTO, String requestSn) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }

        // 1、生成【公章信息】请求参数
        SealInfo sealInfo = sealInfoService.selectBySealCode(cfcaSignTO.getSealCode());
        cfcaSignTO.setImageCode(sealInfo.getExt1());
        cfcaSignTO.setSealImageSize(sealInfo.getExt2());
        String namedSealInfoXML = GenerateUtil.generateOfficialSealInfoXml(cfcaSignTO);
        byte[] image = sealInfo.getImage();

        // 2、调用接口更新公章
        String result = null;
        try {
            result = new SealClient(props.sealUrl).autoUpdateSeal(image, namedSealInfoXML);
        } catch (Exception e) {
            log.error("－消息－: requestSn={},调用接口更新公章失败......", requestSn, e);
            return new CFCASignResult(false, e.getMessage());
        }

        // 3、校验接口返回参数
        if (!checkMakeSealResult(result)) {
            String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(result, CFCAConstant.RETURNCODE), StringUtil.getNodeText(result, CFCAConstant.RETURNMSG));
            if ("未知异常".equals(errorMsg)) {
                if (result.contains("error")) {
                    errorMsg = result;
                }
            }
            return new CFCASignResult(false, result);
        } else {
            return new CFCASignResult(true, result);
        }
    }

    public CFCASignResult batchMakeOfficialSeal(CFCASignTO cfcaSignTO, String requestSn) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }
        String result = null;
        try {
            // 1、生成【公章证书】请求参数
            String certApplyXML = GenerateUtil.generateOfficialCertApplyXml(cfcaSignTO);

            // 2、生成【公章信息】请求参数
            String sealInfoXML = GenerateUtil.generateOfficialSealInfoXml(cfcaSignTO);

            // 3、调用接口生成公章
            result = new SealClient(props.sealUrl).makeSealAutomation(certApplyXML, cfcaSignTO.getSealImage(), sealInfoXML);
        } catch (Exception e) {
            log.error("－消息－: requestSn={},调用接口制公章失败:", requestSn, e);
            throw new RuntimeException(e);
        }

        // 4、校验接口返回参数
        if (!checkMakeSealResult(result)) {
            String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(result, CFCAConstant.RETURNCODE), StringUtil.getNodeText(result, CFCAConstant.RETURNMSG));
            if (errorMsg.equals("未知异常")) {
                if (result.contains("error")) {
                    errorMsg = result;
                }
            }
            log.error("－消息－: requestSn={},制公章失败:{}", requestSn, errorMsg);
            CFCASignResult cfcaSignResult = new CFCASignResult(false, errorMsg);
            return cfcaSignResult;
        }
        return new CFCASignResult(true, "制公章成功");
    }


    public CFCASignResult batchMakePersonalSeal(CFCASignTO cfcaSignTO, String requestSn) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }
        // 1、生成【私章证书】请求参数
        String certApplyXML = GenerateUtil.generatePersonalCertApplyXml(cfcaSignTO);

        // 2、生成【私章信息】请求参数
        String namedSealInfoXML = GenerateUtil.generatePersonalSealStrategyXML(cfcaSignTO);

        // 3、调用接口生成私章
        String result = null;
        try {
            result = new SealClient(props.sealUrl).makeNamedSealAutomation(certApplyXML, namedSealInfoXML);
        } catch (Exception e) {
            log.error("－消息－: requestSn={},调用接口制私章失败......", requestSn, e);
            return new CFCASignResult(false, e.getMessage());
        }

        // 4、校验接口返回参数
        if (!checkMakeSealResult(result)) {
            String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(result, CFCAConstant.RETURNCODE), StringUtil.getNodeText(result, CFCAConstant.RETURNMSG));
            if ("未知异常".equals(errorMsg)) {
                if (result.contains("error")) {
                    errorMsg = result;
                }
            }
            // 5、打印具体制章人信息
            JSONObject object = new JSONObject();
            object.put("userId", cfcaSignTO.getUserIdNo());
            object.put("userName", cfcaSignTO.getUserName());
            object.put("idTypeCode", cfcaSignTO.getIdTypeCode());
            object.put("sealFlag", StringUtil.getNodeText(namedSealInfoXML, "SealFlag"));
            log.error("－消息－: requestSn={},制私章失败:【{}】; 申请人信息={}", requestSn, errorMsg, object.toJSONString());
            return new CFCASignResult(false, result);
        } else {
            return new CFCASignResult(true, result);
        }
    }

    public CFCASignResult batchOfficialSign(CFCASignParam param) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }
        String noSignPDFPath = param.getNoSignPDFPath();
        String requestSn = param.getRequestSn();
        for (CFCASignTO cfcaSignTO : param.getCfcaSealInfoTOList()) {
            // 1、生成签章请求参数
            String sealStrategyXML = GenerateUtil.generateOfficialStrategyXML(cfcaSignTO, CFCAConstant.cfca_offical_offset_x, CFCAConstant.cfca_offical_offset_y, CFCAConstant.cfca_offical_code_style);

            // 2、获取待签章pdf文件流
            byte[] pdfBytes = getNoSignPDFBytes(requestSn, noSignPDFPath);

            // 3、调用三方签公章
            byte[] sealedPdf = invokeRemoteSign(param, sealStrategyXML, pdfBytes, true, param.getCfcaSealInfoTOList().size());

            // 4、校验三方签约结果
            if (!StringUtil.checkResultDataValid(sealedPdf)) {
                String sealedPdfStr = null;
                sealedPdfStr = new String(sealedPdf, StandardCharsets.UTF_8);
                String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(sealedPdfStr, CFCAConstant.CFCA_ERROR_CODE), StringUtil.getNodeText(sealedPdfStr, CFCAConstant.RETURNMSG));
                return new CFCASignResult(false, errorMsg + "-【公章】");
            }

            // 5、将已经签完章的流会写到pdf文件
            String signPDFPath = param.getSignPDFPath();
            coverPDF(requestSn, signPDFPath, sealedPdf);
            noSignPDFPath = param.getSignPDFPath();
        }
        log.debug("－消息－: requestSn={},CFCA签章证书保存路径:{}", requestSn, param.getSignPDFPath());
        return new CFCASignResult(true, "签章成功");
    }

    public CFCASignResult batchPersonalSign(CFCASignParam param) throws Exception {
        if (props.sealUrl.equals("empty")) {
            throw new RuntimeException("未配置签章服务URL......");
        }
        // 1、生成签章请求参数
        String requestSn = param.getRequestSn();
        String batchSealStrategyXML = GenerateUtil.generatePersonalStrategyXML(param, CFCAConstant.cfca_personal_location,CFCAConstant.cfca_personal_offset_x, CFCAConstant.cfca_personal_offset_y, CFCAConstant.cfca_personal_code_style);

        // 2、获取待签章pdf文件流
        byte[] pdfBytes = getNoSignPDFBytes(requestSn, param.getNoSignPDFPath());

        // 3、调用三方签私章
        byte[] sealedPdf = invokeRemoteSign(param, batchSealStrategyXML, pdfBytes, false, param.getCfcaSealInfoTOList().size());

        // 4、校验三方签约结果
        if (!StringUtil.checkResultDataValid(sealedPdf)) {
            String sealedPdfStr = new String(sealedPdf, StandardCharsets.UTF_8);
            String errorMsg = ErrorCodeHelper.parseErrorCode(StringUtil.getNodeText(sealedPdfStr, CFCAConstant.CFCA_ERROR_CODE), StringUtil.getNodeText(sealedPdfStr, CFCAConstant.RETURNMSG));
            // 补丁修复: 解决中金seal_apply_info表没有,本地表有的情况
            if (errorMsg.contains("没有找到对应的印章")) {
                for (CFCASignTO cfcaSignTO : param.getCfcaSealInfoTOList()) {
                    SealCertInfoRo sealCertInfo = this.querySealApplyInfo(requestSn, cfcaSignTO.getUserName(), cfcaSignTO.getIdTypeCode(), cfcaSignTO.getUserIdNo());
                    SealApplyInfoDTO sealApplyInfo = certInfoService.getSealInfo(cfcaSignTO.getIdTypeCode(), cfcaSignTO.getUserIdNo(), cfcaSignTO.getUserName());
                    if (Objects.isNull(sealCertInfo) && Objects.nonNull(sealApplyInfo)) {
                        certInfoService.deleteByIdTypeAndNo(cfcaSignTO.getIdTypeCode(), cfcaSignTO.getUserIdNo());
                        JSONObject object = new JSONObject();
                        object.put("userId", cfcaSignTO.getUserIdNo());
                        object.put("userName", cfcaSignTO.getUserName());
                        object.put("idTypeCode", cfcaSignTO.getIdTypeCode());
                        log.info("－消息－: requestSn={},删除本地表有,中金表没有差异数据成功,申请人信息={}", requestSn, object.toJSONString());
                    }
                }
            }
            return new CFCASignResult(false, errorMsg + "-【私章】");
        }

        // 5、将已经签完章的流会写到pdf文件
        String signPDFPath = param.getSignPDFPath();
        coverPDF(requestSn, signPDFPath, sealedPdf);
        log.debug("－消息－: requestSn={},CFCA签章证书保存路径:{}", requestSn, param.getSignPDFPath());
        return new CFCASignResult(true, "签章成功");
    }

    /**
     * RPC调用中金签章服务
     *
     * @param sealStrategyXML
     * @param pdfBytes
     * @param isOfficial
     * @return
     * @throws Exception
     */
    private byte[] invokeRemoteSign(CFCASignParam param, String sealStrategyXML, byte[] pdfBytes, boolean isOfficial, int sealNum) throws Exception {
        byte[] sealedPdf;
        String sealType = isOfficial == true ? "公章" : "个人章";
        String requestSn = param.getRequestSn();
        try {
            long start = System.currentTimeMillis();
            if (isOfficial) {
                sealedPdf = new SealClient(props.sealUrl).sealAutoPdf(pdfBytes, sealStrategyXML);
            } else {
                sealedPdf = new SealClient(props.sealUrl).batchSealAutoPdf(pdfBytes, sealStrategyXML);
            }
            long end = System.currentTimeMillis();
            log.debug("－消息－: requestSn={},自动化签章【" + sealType + "】完成,pdf文档大小:{},签署章个数:{},耗时:{}毫秒", requestSn, GeneralUtil.fileSize(pdfBytes.length), sealNum, (end - start));
        } catch (Exception e) {
            log.error(LogUtil.outPut(requestSn, "调用三方签章服务失败"));
            throw new RuntimeException(e);
        }
        return sealedPdf;
    }

    /**
     * 校验接口返回信息
     *
     * @param result
     */
    private boolean checkMakeSealResult(String result) {
        if (CFCAConstant.CFCA_SUCCESS.equals(StringUtil.getNodeText(result, CFCAConstant.RETURNCODE))) {
            return true;
        }
        return false;
    }

    /**
     * 覆盖pdf文件
     *
     * @param requestSn
     * @param signPDFPath
     * @param sealedPdf
     * @throws Exception
     */
    private void coverPDF(String requestSn, String signPDFPath, byte[] sealedPdf) throws Exception {
        try {
            FileUtil.wirteDataToFile(signPDFPath, sealedPdf);
        } catch (Exception e) {
            log.error(LogUtil.outPut(requestSn, "覆盖pdf文件失败"));
            throw new RuntimeException(e);
        }
    }

    /**
     * 获取待签章pdf文件流
     *
     * @return
     * @throws Exception
     */
    private byte[] getNoSignPDFBytes(String requestSn, String noSignPDFPath) throws Exception {
        byte[] pdfBytes;
        try {
            pdfBytes = FileUtil.getBytesFromFile(new File(noSignPDFPath));
        } catch (Exception e) {
            log.error(LogUtil.outPut(requestSn, "获取待签章pdf文件流失败"));
            throw new RuntimeException(e);
        }
        return pdfBytes;
    }

}
